/*
 *    This file is part of BlobDB.
 *
 *    BlobDB is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    BlobDB is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with BlobDB.  If not, see <http://www.gnu.org/licenses/>.
 *    
 *    Copyright(c) 2011 Michael Kankkonen
 *    http://www.vnetcon.org
 */


package org.vnetcon.blobdb.database.util.parser;

import java.util.ArrayList;
import java.util.List;

import org.vnetcon.blobdb.database.SQLException;
import org.vnetcon.blobdb.database.util.ColumnInfo;
import org.vnetcon.blobdb.database.util.SQLParser;
import org.vnetcon.blobdb.database.util.TableRow;

public class CreateTableParser {

	private ArrayList<String> elements = null;
	private ArrayList<String> foreignTables = new ArrayList<String>();

	private String table = null;
	private ColumnInfo columnInfo = new ColumnInfo();
	
	private int rowsize = -1;
	private int segmaxrows = -1;
	
	public CreateTableParser(ArrayList<String> elements) throws SQLException{
		this.elements = elements;
		init();
	}
	
	private void init() throws SQLException{
		initTable();
		initCols();
	}

	private void initCols() throws SQLException {
		int i = 8;
		int iTotSize = 0;
		String strBuf = null;
		
		while(i < elements.size()){
			i++;
			String columnName = elements.get(i).trim();
			i++;
			String columnType = elements.get(i).trim();
			int iType = -1;
			int iSize = -1;
			int iDecimals = 0;
			
			if(!columnType.equals(SQLParser.CHAR_COL)
					&& !columnType.equals(SQLParser.NUMBER_COL)
					&& !columnType.equals(SQLParser.DECIMAL_COL)
					&& !columnType.equals(SQLParser.DATE_COL)
					&& !columnType.equals(SQLParser.TIME_COL)
					&& !columnType.equals(SQLParser.FOREIGN_KEY_COL)
					){
				throw new SQLException("Invalid datatype: " + columnType);
			}

			if(columnType.equals(SQLParser.CHAR_COL)){
				iType = ColumnInfo.CHAR;
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals("(")){
					throw new SQLException("Expecting '(' after column type: " + columnName + " " + columnType);
				}
				i++;
				strBuf = elements.get(i).trim();
				try{
					iSize = Integer.parseInt(strBuf);
				}catch(Exception ex){
					throw new SQLException("Invalid column type length: " + strBuf);
				}
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(")")){
					throw new SQLException("Expecting ')' after column type size: " + columnName + " " + columnType);
				}
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(",") && !strBuf.equals(")")){
					throw new SQLException("Expecting ',' or ')' after column type size: " + columnName + " " + columnType);
				}
				if(strBuf.equals(")")) {
					i++;
					strBuf = elements.get(i).trim();
					if(!strBuf.equals(";")) {
						throw new SQLException("Expecting ';' to end sql table create statement.");
					}
				}
				if(strBuf.equals(",")) {
					i--;
				}
				columnInfo.addColumn(columnName, iType, iSize, 0);
				iTotSize += iSize;
			}
			if(columnType.equals(SQLParser.NUMBER_COL)){
				iType = ColumnInfo.NUMBER;
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals("(")){
					throw new SQLException("Expecting '(' after column type: " + columnName + " " + columnType);
				}
				i++;
				strBuf = elements.get(i).trim();
				try{
					iSize = Integer.parseInt(strBuf);
				}catch(Exception ex){
					throw new SQLException("Invalid column type length: " + strBuf);
				}
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(")")){
					throw new SQLException("Expecting ')' after column type size: " + columnName + " " + columnType);
				}
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(",") && !strBuf.equals(")")){
					throw new SQLException("Expecting ',' or ')' after column type size: " + columnName + " " + columnType);
				}
				if(strBuf.equals(")")) {
					i++;
					strBuf = elements.get(i).trim();
					if(!strBuf.equals(";")) {
						throw new SQLException("Expecting ';' to end sql table create statement.");
					}
				}
				if(strBuf.equals(",")) {
					i--;
				}
				columnInfo.addColumn(columnName, iType, iSize, 0);
				iTotSize += iSize;
			}
			if(columnType.equals(SQLParser.DECIMAL_COL)){
				iType = ColumnInfo.DECIMAL;
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals("(")){
					throw new SQLException("Expecting '(' after column type: " + columnName + " " + columnType);
				}
				i++;
				strBuf = elements.get(i).trim();
				try{
					String parts[] = strBuf.split("\\.");
					iSize = Integer.parseInt(parts[0]) + 1;
					iDecimals = Integer.parseInt(parts[1]);
				}catch(Exception ex){
					throw new SQLException("Invalid column type length: " + strBuf);
				}
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(")")){
					throw new SQLException("Expecting ')' after column type size: " + columnName + " " + columnType);
				}
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(",") && !strBuf.equals(")")){
					throw new SQLException("Expecting ',' or ')' after column type size: " + columnName + " " + columnType);
				}
				if(strBuf.equals(")")) {
					i++;
					strBuf = elements.get(i).trim();
					if(!strBuf.equals(";")) {
						throw new SQLException("Expecting ';' to end sql table create statement.");
					}
				}
				if(strBuf.equals(",")) {
					i--;
				}
				columnInfo.addColumn(columnName, iType, iSize, iDecimals);
				iTotSize += iSize + iDecimals + 1;
			}
			if(columnType.equals(SQLParser.DATE_COL)){
				iType = ColumnInfo.DATE;
				iSize = 8;
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(",") && !strBuf.equals(")")){
					throw new SQLException("Expecting ',' or ')' after column type size: " + columnName + " " + columnType);
				}
				if(strBuf.equals(")")) {
					i++;
					strBuf = elements.get(i).trim();
					if(!strBuf.equals(";")) {
						throw new SQLException("Expecting ';' to end sql table create statement.");
					}
				}
				if(strBuf.equals(",")) {
					i--;
				}
				columnInfo.addColumn(columnName, iType, iSize, 0);
				iTotSize += iSize;
			}
			if(columnType.equals(SQLParser.TIME_COL)){
				iType = ColumnInfo.TIME;
				iSize = 6;
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(",") && !strBuf.equals(")")){
					throw new SQLException("Expecting ',' or ')' after column type size: " + columnName + " " + columnType);
				}
				if(strBuf.equals(")")) {
					i++;
					strBuf = elements.get(i).trim();
					if(!strBuf.equals(";")) {
						throw new SQLException("Expecting ';' to end sql table create statement.");
					}
				}
				if(strBuf.equals(",")) {
					i--;
				}
				columnInfo.addColumn(columnName, iType, iSize, 0);
				iTotSize += iSize;
			}
			if(columnType.equals(SQLParser.FOREIGN_KEY_COL)){
				iType = ColumnInfo.FOREIGN_KEY;
				iSize = TableRow.ROWID_SIZE;
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals("(")){
					throw new SQLException("Expecting '(' after column type: " + columnName + " " + columnType);
				}
				i++;
				strBuf = elements.get(i).trim();
				try{
					//TODO: add table check for foreign key table
					this.foreignTables.add(strBuf);
				}catch(Exception ex){
					throw new SQLException("Invalid table name for foreign key column: " + strBuf);
				}
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(")")){
					throw new SQLException("Expecting ')' after column type size: " + columnName + " " + columnType);
				}
				i++;
				strBuf = elements.get(i).trim();
				if(!strBuf.equals(",") && !strBuf.equals(")")){
					throw new SQLException("Expecting ',' or ')' after column type size: " + columnName + " " + columnType);
				}
				if(strBuf.equals(")")) {
					i++;
					strBuf = elements.get(i).trim();
					if(!strBuf.equals(";")) {
						throw new SQLException("Expecting ';' to end sql table create statement.");
					}
				}
				if(strBuf.equals(",")) {
					i--;
				}
				columnInfo.addColumn(columnName, iType, iSize, 0);
				iTotSize += iSize;
			}
			i++;
		}
		if(iTotSize > this.rowsize){
			throw new SQLException("Rowsize must not exceed " + rowsize + " bytes.");
		}
	}
	
	private void initTable() throws SQLException {
		String buf = null;
		table = elements.get(2);
		buf = elements.get(3);
		if(!buf.equals("(")){
			throw new SQLException("Expecting ( after tablename");
		}
		buf = elements.get(4);
		try{
			this.rowsize = Integer.parseInt(buf);
		}catch(Exception ex){
			throw new SQLException("Invalid max rowsize value: " + buf);
		}
		buf = elements.get(5);
		if(!buf.equals(",")){
			throw new SQLException("Expecting , after max rowsize");
		}
		buf = elements.get(6);
		try{
			this.segmaxrows = Integer.parseInt(buf);
		}catch(Exception ex){
			throw new SQLException("Invalid max rowcount for segment value: " + buf);
		}
		buf = elements.get(7);
		if(!buf.equals(")")){
			throw new SQLException("Expecting ) after max rowcount for segment value. Found " + buf);
		}
		buf = elements.get(8);
		if(!buf.equals("(")){
			throw new SQLException("Expecting ( before column definitions. Found " + buf);
		}
		
	}
	
	public String getTable(){
		return this.table;
	}
	
	public ColumnInfo getColumnInfo(){
		return this.columnInfo;
	}

	public int getRowsize() {
		return rowsize;
	}

	public int getSegmaxrows() {
		return segmaxrows;
	}

	public ArrayList<String> getForeignTables() {
		return foreignTables;
	}


}
